package org.fuxia.im.core.socket.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Arrays;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.apache.log4j.Logger;
import org.fuxia.im.core.ECMessageType;
import org.fuxia.im.core.entries.ECPacket;
import org.fuxia.im.core.entries.ECResponse;
import org.fuxia.im.core.iconst.Constant;
import org.fuxia.im.core.listeners.Listener;
import org.fuxia.im.core.socket.ECClient;
import org.fuxia.im.core.utils.MessageGZIP;
import org.fuxia.im.core.utils.ObjectUtil;

import com.alibaba.fastjson.JSON;

public class ECNioClient implements ECClient
{
	private static final Logger				logger		= Logger.getLogger(ECNioClient.class);
	private static final int				PSIZE		= 4;
	private final SocketChannel				client;
	private Selector						selector;
	private static ByteBuffer				buffer		= ByteBuffer.allocate(1024 * 1024);
	private volatile boolean				isStart		= false;
	private ConcurrentMap<String, Listener>	listeners	= null;
	private ConcurrentMap<Object, Object>	context		= new ConcurrentHashMap<Object, Object>();

	//private static WritableByteChannel		out		= Channels.newChannel(System.out);

	public ECNioClient(String address,int port) throws IOException
	{
		this.client = SocketChannel.open(new InetSocketAddress(address, port));
		this.client.socket().setReceiveBufferSize(1024 * 1024);
		this.client.configureBlocking(false);
		this.selector = Selector.open();
		this.client.register(selector, SelectionKey.OP_READ);
		isStart = true;
		listeners = new ConcurrentHashMap<String, Listener>();
		this.context.put(ECPacket.class.getName(), ECPacket.class);
		this.context.put(ECResponse.class.getName(), ECResponse.class);
	}

	public void registPacketListener(Listener listener , String pkType)
	{
		listeners.putIfAbsent(pkType, listener);
	}

	public void registResponseListener(Listener listener , String reponseType)
	{
		listeners.put(reponseType, listener);
	}

	public void sendMessage(ECPacket message) throws Exception
	{
		logger.info(message);
		byte[] b = ObjectUtil.getByteByJson(message);
		ByteBuffer send = ByteBuffer.allocate(b.length + PSIZE);
		send.putInt(b.length);
		send.put(b);
		send.flip();
		this.client.write(send);
	}

	public void sendTextMessage(String message , String from , String to[]) throws Exception
	{
		ECPacket packet = createTextMsgPacket(message, from, to) ;
		this.sendMessage(packet);
	}
	

	private ECPacket createTextMsgPacket(String message,String from,String to[]) {
		ECPacket packet = new ECPacket();
		packet.setMessageType(ECMessageType.PLAINMESSAGE.name());
		packet.addParam("content", message);
		packet.setFrom(from);
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < to.length - 1; i++)
		{
			sb.append(to[i]);
			sb.append("#");
		}
		sb.append(to[to.length - 1]);
		packet.setTo(sb.toString());
		return packet;
	}
	
	public void sendGroupTextMessage(String message,String from,String groupId,String to[]) throws Exception{
		ECPacket packet = createTextMsgPacket(message, from, to) ;
		packet.addParam("groupId", groupId);
		this.sendMessage(packet);
	}



	public Object parseObject(String jsonStr)
	{
		int classNameIndex = jsonStr.indexOf(Constant.KEYNAME.name());
		int firstMaoIndex = jsonStr.indexOf(":", classNameIndex);
		String className = jsonStr.substring(firstMaoIndex + 2, jsonStr.indexOf("\"", firstMaoIndex + 2));
		Class<?> clazz = (Class<?>) context.get(className);
		Object obj = JSON.parseObject(jsonStr, clazz);
		return obj;
	}

	public void run()
	{
		while (isStart)
		{
			try
			{
				while (selector.select() > 0)
				{
					Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();
					while (keyIter.hasNext())
					{
						SelectionKey s = keyIter.next();
						if (s.isReadable())
						{
							SocketChannel sc = (SocketChannel) s.channel();
							buffer.clear();
							sc.read(buffer);
							buffer.flip();
							byte[] objs = Arrays.copyOf(buffer.array(), buffer.limit());
							String jsonStr = MessageGZIP.uncompressToString(objs);
							try
							{
								Object recvEntry = parseObject(jsonStr);
								if (recvEntry.getClass().isAssignableFrom(ECPacket.class))
								{
									logger.info("收到一个分组数据包,将调用分组数据包的监听器");
									ECPacket p = ((ECPacket) recvEntry);
									Listener l = listeners.get(p.getMessageType());
									if (l != null)
									{
										l.trigger(p);
									}
								}
								else if (recvEntry.getClass().isAssignableFrom(ECResponse.class))
								{
									ECResponse resp = (ECResponse) recvEntry;
									System.out.println(resp);
									Listener l = listeners.get(resp.getResponseType());
									if (l != null)
									{
										l.trigger(resp);
									}
									else
									{
										// l = listeners.get("RESPONSE");
										// l.trigger(resp);
									}
								}
							}
							catch (Exception e)
							{
								e.printStackTrace();
								logger.error("[错误]：数据包格式错误");
							}
						}
						keyIter.remove();
					}
				}
			}
			catch (IOException e)
			{
				e.printStackTrace();
			}
		}
	}

	public void shutdown()
	{
		isStart = false;
	}
}
